package com.xh3140.android.widget.button;

import android.content.Context;
import android.content.res.ColorStateList;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.GradientDrawable;
import android.graphics.drawable.InsetDrawable;
import android.graphics.drawable.RippleDrawable;
import android.graphics.drawable.StateListDrawable;
import android.os.Build;
import android.util.AttributeSet;

import androidx.annotation.ColorInt;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.Px;
import androidx.annotation.Size;
import androidx.appcompat.widget.AppCompatButton;

import com.xh3140.android.widget.R;


/**
 * 按钮控件
 */
@SuppressWarnings("unused")
public class Button extends AppCompatButton {
    /**
     * 默认状态背景颜色
     */
    private int mDefaultColor;

    /**
     * 按下状态背景颜色
     */
    private int mPressedColor;

    /**
     * 是否是波纹背景
     * true {@link RippleDrawable}
     * false {@link StateListDrawable}
     */
    private boolean mIsRippleDrawable = true;

    /**
     * 是否是内嵌背景
     * true {@link InsetDrawable}
     * false {@link GradientDrawable}
     */
    private boolean mIsInsetDrawable = true;

    /**
     * 圆角半径
     * 格式：[leftTop, rightTop, rightBottom, leftBottom] px
     */
    private int[] mRadius = {0, 0, 0, 0};

    /**
     * 圆角半径
     * 格式：[start, top, end, bottom] px
     */
    private int[] mInset = {0, 0, 0, 0};

    /**
     * 在按钮组中的位置
     */
    private GroupLocation mGroupLocation;

    /**
     * 是否需要设置背景
     */
    private boolean mNeedUpdateBackground = true;

    /**
     * 在按钮组中的位置枚举类
     */
    public enum GroupLocation {
        // 最左边，表示在水平按钮组的最左边
        LEFT(new int[]{1, 0, 0, 1, 1, 1, 0, 1}),

        // 水平之间，表示在水平按钮组的最左和最右按钮之间
        HORIZONTAL_BETWEEN(new int[]{0, 0, 0, 0, 0, 1, 0, 1}),

        // 最右边，表示在水平按钮组的最右边
        RIGHT(new int[]{0, 1, 1, 0, 0, 1, 1, 1}),

        // 顶部，表示在竖直按钮组的顶部
        TOP(new int[]{1, 1, 0, 0, 1, 1, 1, 0}),

        // 竖直之间，表示在竖直按钮组的顶部和底部按钮之间
        VERTICAL_BETWEEN(new int[]{0, 0, 0, 0, 1, 0, 1, 0}),

        // 底部，表示在竖直按钮组的底部
        BOTTOM(new int[]{0, 0, 1, 1, 1, 0, 1, 1});

        /**
         * radius,inset调节因子,共8个值。
         * 前4个调节radius，后4个调节inset。
         */
        int[] factory;

        GroupLocation(int[] factory) {
            this.factory = factory;
        }
    }

    /**
     * 颜色风格枚举类
     * 可以添加更多好看的主题
     */
    public enum ColorTheme {
        // 亮色
        LIGHT(0xFF212529, 0xFFF8F9FA, 0xFFE2E6EA),

        // 暗色
        DARK(0xFFFFFFFF, 0xFF343A40, 0xFF23272B),

        // 灰色
        GRAY(0xFFFFFFFF, 0xFF6C757D, 0xFF5A6268),

        // 红色
        RED(0xFFFFFFFF, 0xFFDC3545, 0xFFC82333),

        // 绿色
        GREEN(0xFFFFFFFF, 0xFF28A745, 0xFF218838),

        // 蓝色
        BLUE(0xFFFFFFFF, 0xFF007BFF, 0xFF0069A9),

        // 蓝绿色
        CYAN(0xFFFFFFFF, 0xFF17A2B8, 0xFF138496),

        // 黄色
        YELLOW(0xFF212529, 0xFFFFC107, 0xFFE0A800),

        // 紫色
        PURPLE(0xFFFFFFFF, 0xFF6200EE, 0xFF3700B3),

        // 粉红色
        PINK(0xFF800000, 0xFFFFB6C1, 0xFFFFC0CB),

        // 金色
        GOLD(0xFFFFFFFF, 0xFFFFD700, 0xFFDAA520),

        // 橙色
        ORANGE(0xFFFFFFFF, 0xFFFFA500, 0xFFFF8C00),

        // 橄榄色
        OLIVE(0xFFFFFFFF, 0xFF808000, 0xFF556B2F),

        // 棕色
        BROWN(0xFFFFFFFF, 0xFFA52A2A, 0xFF800000);

        /**
         * 字体颜色 argb
         */
        @ColorInt
        public final int textColor;

        /**
         * 默认状态下的背景颜色 argb
         */
        @ColorInt
        public final int defaultColor;

        /**
         * 按下状态下的背景颜色 argb
         */
        @ColorInt
        public final int pressedColor;

        ColorTheme(int textColor, int defaultColor, int pressedColor) {
            this.textColor = textColor;
            this.defaultColor = defaultColor;
            this.pressedColor = pressedColor;
        }
    }

    /**
     * 构造函数
     *
     * @param context    上下文
     * @param text       按钮文本
     * @param colorTheme 按钮颜色主题
     */
    public Button(@NonNull Context context, @Nullable CharSequence text, @NonNull ColorTheme colorTheme) {
        this(context);
        setText(text);
        setThemeColor(colorTheme);
    }

    public Button(@NonNull Context context) {
        this(context, null);
    }

    public Button(@NonNull Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, androidx.appcompat.R.attr.buttonStyle);
    }

    public Button(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        final TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.Button, 0, R.style.Button);
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
            saveAttributeDataForStyleable(context, R.styleable.Button, attrs, a, defStyleAttr, R.style.Button);
        }
        try {
            mDefaultColor = a.getColor(R.styleable.Button_defaultColor, mDefaultColor);
            mPressedColor = a.getColor(R.styleable.Button_pressedColor, mPressedColor);
            mIsRippleDrawable = a.getBoolean(R.styleable.Button_isRipple, mIsRippleDrawable);
            mIsInsetDrawable = a.getBoolean(R.styleable.Button_isInset, mIsInsetDrawable);
            if (a.hasValue(R.styleable.Button_radius)) {
                final int radius = a.getDimensionPixelSize(R.styleable.Button_radius, -1);
                mRadius = new int[]{radius, radius, radius, radius};
            } else {
                mRadius[0] = a.getDimensionPixelSize(R.styleable.Button_radius_leftTop, mRadius[0]);
                mRadius[1] = a.getDimensionPixelSize(R.styleable.Button_radius_rightTop, mRadius[1]);
                mRadius[2] = a.getDimensionPixelSize(R.styleable.Button_radius_rightBottom, mRadius[2]);
                mRadius[3] = a.getDimensionPixelSize(R.styleable.Button_radius_leftBottom, mRadius[3]);
            }
            if (a.hasValue(R.styleable.Button_inset)) {
                final int inset = a.getDimensionPixelSize(R.styleable.Button_inset, -1);
                mInset = new int[]{inset, inset, inset, inset};
            } else {
                mInset[0] = a.getDimensionPixelSize(R.styleable.Button_inset_left, mInset[0]);
                mInset[1] = a.getDimensionPixelSize(R.styleable.Button_inset_top, mInset[1]);
                mInset[2] = a.getDimensionPixelSize(R.styleable.Button_inset_right, mInset[2]);
                mInset[3] = a.getDimensionPixelSize(R.styleable.Button_inset_bottom, mInset[3]);
            }
            if (a.hasValue(R.styleable.Button_groupLocation)) {
                final int locationIndex = a.getInt(R.styleable.Button_groupLocation, -1);
                mGroupLocation = GroupLocation.values()[locationIndex];
            }
            if (a.hasValue(R.styleable.Button_colorTheme)) {
                final int themeIndex = a.getInt(R.styleable.Button_colorTheme, -1);
                final ColorTheme theme = ColorTheme.values()[themeIndex];
                mDefaultColor = theme.defaultColor;
                mPressedColor = theme.pressedColor;
                setTextColor(theme.textColor);
            }
        } finally {
            a.recycle();
        }
    }

    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        super.onLayout(changed, left, top, right, bottom);
        if (mGroupLocation != null) {
            // 调节圆角半径
            mRadius[0] *= mGroupLocation.factory[0];
            mRadius[1] *= mGroupLocation.factory[1];
            mRadius[2] *= mGroupLocation.factory[2];
            mRadius[3] *= mGroupLocation.factory[3];
            // 调节内嵌距离
            mInset[0] *= mGroupLocation.factory[4];
            mInset[1] *= mGroupLocation.factory[5];
            mInset[2] *= mGroupLocation.factory[6];
            mInset[3] *= mGroupLocation.factory[7];
        }
        // 需要更新背景
        mNeedUpdateBackground = true;
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        // onDraw 事件会频繁的触发，必须设置限制，不然背景的点击效果不会有时间响应
        if (mNeedUpdateBackground) {
            mNeedUpdateBackground = false;
            // 设置背景，如果不在这个位置设置，会造成按钮文本不能居中的状况
            updateInnerThemeBackground();
        }
    }

    /**
     * 获取内部背景
     *
     * @param backgroundColor 背景颜色
     */
    private Drawable getRectContentDrawable(ColorStateList backgroundColor) {
        final GradientDrawable drawable = new GradientDrawable();
        drawable.setColor(backgroundColor);
        drawable.setCornerRadii(new float[]{
                mRadius[0], mRadius[0],
                mRadius[1], mRadius[1],
                mRadius[2], mRadius[2],
                mRadius[3], mRadius[3]
        });
        if (!mIsInsetDrawable) {
            return drawable;
        }
        return new InsetDrawable(drawable, mInset[0], mInset[1], mInset[2], mInset[3]);
    }

    /**
     * 更新内部主题背景
     */
    private void updateInnerThemeBackground() {
        final ColorStateList defaultColor = ColorStateList.valueOf(mDefaultColor);
        final ColorStateList pressedColor = ColorStateList.valueOf(mPressedColor);
        if (mIsRippleDrawable) {
            setBackground(new RippleDrawable(pressedColor, getRectContentDrawable(defaultColor), null));
        } else {
            final StateListDrawable drawable = new StateListDrawable();
            drawable.addState(new int[]{-android.R.attr.state_pressed}, getRectContentDrawable(defaultColor));
            drawable.addState(new int[]{android.R.attr.state_pressed}, getRectContentDrawable(pressedColor));
            setBackground(drawable);
        }
    }

    /**
     * 设置是否是波纹背景
     * true {@link RippleDrawable}
     * false {@link StateListDrawable}
     *
     * @param isRipple 是否是波纹背景
     */
    public void setIsRippleDrawable(boolean isRipple) {
        if (mIsRippleDrawable != isRipple) {
            mIsRippleDrawable = isRipple;
            requestLayout();
        }
    }

    /**
     * 设置是否是内嵌背景
     * true {@link InsetDrawable}
     * false {@link GradientDrawable}
     *
     * @param isInset 是否是内嵌背景
     */
    public void setIsInsetDrawable(boolean isInset) {
        if (mIsInsetDrawable != isInset) {
            mIsInsetDrawable = isInset;
            requestLayout();
        }
    }

    /**
     * 设置圆角半径
     * 格式：[leftTop, rightTop, rightBottom, leftBottom] px
     *
     * @param radius 圆角半径 px
     */
    public void setRadius(@NonNull @Size(4) int[] radius) {
        if (mRadius[0] != radius[0] || mRadius[1] != radius[1] || mRadius[2] != radius[2] || mRadius[3] != radius[3]) {
            mRadius = radius;
            requestLayout();
        }
    }

    /**
     * 设置圆角半径
     *
     * @param leftTop     左上角的圆角半径 px
     * @param rightTop    右上角的圆角半径 px
     * @param rightBottom 右下角的圆角半径 px
     * @param leftBottom  左下角的圆角半径 px
     */
    public void setRadius(@Px int leftTop, @Px int rightTop, @Px int rightBottom, @Px int leftBottom) {
        setRadius(new int[]{leftTop, rightTop, rightBottom, leftBottom});
    }

    /**
     * 设置圆角半径
     *
     * @param radius 四个角的圆角半径 px
     */
    public void setRadius(@Px int radius) {
        setRadius(new int[]{radius, radius, radius, radius});
    }

    /**
     * 设置内嵌距离
     * 格式：[left, top, right, bottom] dp
     *
     * @param inset 内嵌距离 px
     */
    public void setInset(@NonNull @Size(4) int[] inset) {
        if (mInset[0] != inset[0] || mInset[1] != inset[1] || mInset[2] != inset[2] || mInset[3] != inset[3]) {
            mInset = inset;
            requestLayout();
        }
    }

    /**
     * 设置内嵌距离
     *
     * @param left   左方的内嵌距离 px
     * @param top    上方的内嵌距离 px
     * @param right  右方的内嵌距离 px
     * @param bottom 下方的内嵌距离 px
     */
    public void setInset(@Px int left, @Px int top, @Px int right, @Px int bottom) {
        setInset(new int[]{left, top, right, bottom});
    }

    /**
     * 设置内嵌距离
     *
     * @param inset 四方的内嵌距离 px
     */
    public void setInset(@Px int inset) {
        setInset(new int[]{inset, inset, inset, inset});
    }

    /**
     * 设置在按钮组中的位置
     * 注意，当希望按钮单独体现，即不在按钮组中时，这个值应当为null
     *
     * @param location 按钮在按钮组中的位置
     */
    public void setGroupLocation(@Nullable GroupLocation location) {
        if (mGroupLocation != location) {
            mGroupLocation = location;
            requestLayout();
        }
    }

    /**
     * 设置颜色主题 enum
     * 注意，按钮处于禁用状态时，禁止更改颜色主题
     *
     * @param theme 按钮颜色主题
     */
    public void setThemeColor(@NonNull ColorTheme theme) {
        mNeedUpdateBackground = true;
        mDefaultColor = theme.defaultColor;
        mPressedColor = theme.pressedColor;
        setTextColor(theme.textColor);
    }
}

